#include <stdio.h>
#include <getopt.h>
#include <string.h>
#include <openssl/pem.h>
#include <openssl/evp.h>
#include <openssl/bio.h>
#include <openssl/buffer.h>
#include <openssl/md5.h> 

const char* program_name;

void print_usage(FILE* stream, int exit_code)
{
    fprintf(stream, "Usage: %s options\n", program_name);
    fprintf(stream,
            "-h --help      Display this usage information.\n"
            "-s --sn        sn number.\n"
            "-m --mac       mac address.\n");
    exit(exit_code);
}

signed int generate_private_id(char* serial_number, char* mac_address, char* encrypt_key, char* password){
    MD5_CTX ctx;
    size_t v6;
    unsigned int input[48];
    
    char md5[16];
    
        
    strcpy(input, serial_number);
    strcat(input, mac_address);
    strcat(input, encrypt_key);
    
    MD5_Init(&ctx);
    v6 = strlen((const char *)input);
    MD5_Update(&ctx, input, v6);
    MD5_Final(md5, &ctx);

    
    char base64tmp[60];
    int a = bc_base64_encode(md5, strlen(md5), &base64tmp);
    
    strncpy(password, base64tmp, 8);
    strcpy(&password[8],"\0");
    
}

int bc_base64_encode(const void *data, int data_len, char *buffer)
{
    BIO *b64 = BIO_new(BIO_f_base64());
    BIO *bio = BIO_new(BIO_s_mem());

    bio = BIO_push(b64, bio);
    BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);
    BIO_write(bio, data, data_len);
    BIO_ctrl(bio, BIO_CTRL_FLUSH, 0, NULL);

    BUF_MEM *bptr = NULL;
    BIO_get_mem_ptr(bio, &bptr);

    size_t slen = bptr->length;
    memcpy(buffer, bptr->data, slen);
    
    buffer[slen] = '\0';
    
    for ( int i = 0;  i <= slen; i++ )
    {
        if (toascii(buffer[i]) == 43 )
        {
            buffer[i] =  45;
        }
        else if (toascii(buffer[i])  == 47 )
        {
            buffer[i] =  95;
        }
        
    }
    
    BIO_free_all(bio);
    return slen;
}

int generate_enc_value(char* enc_value){
    strcpy(enc_value, "i8e%Fvj24nz024@d!c");
    return 1;
}

int main(int argc, char* argv[]){

    char* sn = NULL;
    char* mac_addr = NULL;
    
    char enc_value[19];
    char password[8];

    int next_option;
    const char* const short_options="s:m:hv";
    
    const struct option long_options[] = {
        {"sn",      required_argument,  NULL,   's'},
        {"mac",     required_argument,  NULL,   'm'},
        {"help",    no_argument,        NULL,   'h'},
        {"verbose", no_argument,        NULL,   'v'},
        {NULL,      0,  NULL,    0}
    };
    
    int verbose = 0;
   
    program_name = argv[0];
    
    do {
        next_option = getopt_long(argc, argv, short_options, long_options, NULL);

        
        switch (next_option) {
            case 'h':
                print_usage(stdout, 0);
                break;
            case 's':
                if ( strlen(optarg) != 13){
                    printf("invaild sn number\n");
                    print_usage(stdout, 0);
                    abort();
                }
                sn = optarg;
                break;
            case 'm':
                if ( strlen(optarg) != 17){
                    printf("invaild mac address\n");
                    print_usage(stdout, 0);
                    abort();
                }
                mac_addr = optarg;
                break;
            case 'v': /* -v or --verbose */
                verbose = 1;
                break;
            case '?':
                print_usage(stderr, 1);
                break;
      
            default:
                if(!sn || !mac_addr){
                    print_usage(stdout, 0);
                }
                //abort();
                break;
        }
        
    } while (next_option != -1);
    
	  
    generate_enc_value(enc_value);
    
    if (verbose) {
        printf("sn:%s \n", sn);
        printf("mac_addr:%s \n", mac_addr);
        printf("enc_value:%s \n", enc_value);
    }
    
    generate_private_id(sn, mac_addr, enc_value, password);
    
    printf("Password: %s\n", password);
    return 0;
}
